import { app, protocol, BrowserWindow, Menu,ipcMain,ipcRenderer,Tray,dialog } from 'electron';
const {autoUpdater} = require("electron-updater")
const log = require('electron-log');
var os=require('os');

import { createProtocol } from 'vue-cli-plugin-electron-builder/lib';

const packageJson = require("../package.json");
const path = require('path')
const moment = require('moment');
const math = require('mathjs');
import Task from './plugins/task';
import initg from './initg'
import db from './plugins/datastore';
import spotAPI  from './plugins/hb-spot-api';
import common  from './plugins/common';



log.transports.file.level = true;
log.transports.console.level = false;
log.transports.file.file = path.join(os.homedir(),'Desktop','ect.log',);
log.transports.file.maxSize = 104857600;
// log.info('logger init......');
// log.warn('Some problem appears');


let appTitle = "数字货币量化交易机器人";
var tasks = new Map();
ipcMain.on('startupTask',(event,id)=>{

  let task = tasks.get(id);
  if(task){
    return;
  }
  task = new Task(id)
  task.on('message',onMessage);
  task.on('flicker',onFlicker);
  task.on('close',onClose);
  task.on('update',onUpdate);
  task.on('percent',onPercent);
  tasks.set(id,task);
  task.init((success)=>{
    if(success){
       task.startup();
    }
  })
})

ipcMain.on('shutdownTask',(event,id)=>{
  let task = tasks.get(id);
  if(task){
    task.shutdown();
  }
})

ipcMain.on('refreshTaskData',(event,id)=>{
  let task = tasks.get(id);
  if(task){
    task.refreshData();
  }
})

function onMessage(msg){
  let displayer = displayerContainer.get(msg.id);
  if(displayer){
    displayer.webContents.send('message', msg)
  }
}

function onFlicker(msg){
  if(win){
    win.webContents.send('flicker',msg)
  }
}

function onClose(msg){
  let task = tasks.get(msg.id);
  if(task){
    tasks.delete(msg.id)
  }

  if(win){
    win.webContents.send('close',msg)
  }
  let displayer = displayerContainer.get(msg.id);
  if(displayer){
    displayer.webContents.send('updateItem',msg.id)
  }
}

function onUpdate(msg){
  if(win){
    win.webContents.send('updateItem',msg)
  }
  let displayer = displayerContainer.get(msg.id);
  if(displayer){
    displayer.webContents.send('updateItem',msg.id)
  }
}

function onPercent(msg){
  let displayer = displayerContainer.get(msg.id);
  if(displayer){
    displayer.webContents.send('updatePercent',msg)
  }
}



ipcMain.on('getCurrencyBalance',async (event,data)=>{

  let balanceResult = await spotAPI.getOneBalance({'currency':data.currency});
  if(win){
    win.webContents.send('onCurrencyBalanceRes',balanceResult);
  }
  // event.returnValue = balanceResult;
  // if(balanceResult.success){
  //     event.returnValue = new Response(true,balanceResult.data);
  // }else{
  //     event.returnValue = new Response(false,balanceResult.data);
  // }
})
const isDevelopment = process.env.NODE_ENV !== 'production';
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;
let tray = null;
let isTrayQuit = false;
let isUpdateQuit = false;
let isAutoCheckUpdate = true;
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
  { scheme: 'app', privileges: { secure: true, standard: true } },
]);

function createWindow() {
  Menu.setApplicationMenu(null)
  // Create the browser window.
  win = new BrowserWindow({
    icon: path.join(__static, './favicon.ico'),
    title:appTitle,
    width: 930,
    height: 600,
    minWidth:930,
    minHeight:500,
    frame: false,
    // show:true,
    useContentSize:true,
    backgroundColor:'#F7F8FA',
    webPreferences: {
      // Use pluginOptions.nodeIntegration, leave this alone
      // See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
      // nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
      nodeIntegration: true,
      enableRemoteModule: true
      
      // preload: path.join(__dirname, './preload.js'),
      // preload: './preload.js',
    },
   
  });



  if (process.env.WEBPACK_DEV_SERVER_URL) {
    // Load the url of the dev server if in development mode
    win.loadURL(process.env.WEBPACK_DEV_SERVER_URL);
    if (!process.env.IS_TEST) win.webContents.openDevTools();
  } else {
    createProtocol('app');
    // Load the index.html when not in development
    // win.webContents.openDevTools()
    win.loadURL('app://./index.html');
  }
  
  win.on('closed', () => {
    win = null;
  });

  win.on('maximize', function () {
    win.webContents.send('windowMax');
   })
   win.on('unmaximize', function () {
    win.webContents.send('windowUnmax');
   })

   win.on('hide',() => {
    tray.displayBalloon({
      iconType:'info',
      title:appTitle,
      content:'系统最小化到托盘'
    })
   })


}

ipcMain.on('minWin',(event,id)=>{
  if(win != null){
    win.minimize();
  }
})

ipcMain.on('maxWin',(event,id)=>{
  if(win != null){
    //如果窗口的添加了transparent: true配置，每次isMaximized都会返回false
    if(win.isMaximized()){
      win.restore()
    }else{
      win.maximize()
    }
    
  }
})
ipcMain.on('closeWin',(event,id)=>{
  if(win != null){
    win.hide()
  }
})

ipcMain.on('quit',(event,id)=>{
  onQuit()
})




// Quit when all windows are closed.
app.on('window-all-closed', () => {
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (win === null) {
    createWindow();
  }
});

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
  if (isDevelopment && !process.env.IS_TEST) {
    // Install Vue Devtools
    try {
      //await installExtension(VUEJS_DEVTOOLS);
    } catch (e) {
      console.error('Vue Devtools failed to install:', e.toString());
    }
  }
  createWindow();


  //启动授权码检查任务
  loopCheckAuth();

   checkUpdates();
  // openUpdateWin();


});

// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
  if (process.platform === 'win32') {
    process.on('message', (data) => {
      if (data === 'graceful-exit') {
        app.quit();
      }
    });
  } else {
    process.on('SIGTERM', () => {
      app.quit();
    });
  }
}

function onQuit(a,b,c){
   app.quit();
}


app.whenReady().then(() => {
  tray = new Tray(path.join(__static, './favicon.ico'))
  const contextMenu = Menu.buildFromTemplate([
    {label:'交易报表',click:()=>{
      openTradeWin();
    }},
    {label:'行情分析',click:()=>{
      openMarketWin();
    }},
    { type: 'separator' },
    {label:'关于',click:()=>{
      openAboutWin();
    }},
    {label:'帮助',click:()=>{
      dialog.showMessageBoxSync({
        icon: path.join(__static, './favicon.ico'),
        title:'信息',
        message:"帮助功能，敬请期待",
        buttons:['确定'],
        defaultId:0,
        noLink:true
      })
    }},
    {label:'检查更新...',click:()=>{
      isAutoCheckUpdate = false;
      autoUpdater.checkForUpdates();
    }},
    { type: 'separator' },
    
    {label:'退出',click:onQuit},
  ])
  tray.setToolTip(appTitle)
  tray.setContextMenu(contextMenu)
  tray.on('double-click',()=>{
    win.show();
    win.focus()
  })
})

app.on('before-quit',(event)=>{
  
  if(tasks.size > 0){
    //如果有正在运行的任务，不能直接退出。
  dialog.showMessageBoxSync({
      icon: path.join(__static, './favicon.ico'),
      title:'警告',
      message:"有正在运行的任务，为了保证数据准确，请停止任务后再执行退出。",
      buttons:['确定'],
      defaultId:0,
      noLink:true
    })
    event.preventDefault();
    return;
}
  
  if(!isUpdateQuit){
    //如果没有正在远行任务，则显示退出确认窗口
    let checkedIndex = dialog.showMessageBoxSync({
      icon: path.join(__static, './favicon.ico'),
      title:'退出',
      message:"确定退出？",
      buttons:['取消','确定'],
      defaultId:0,
      cancelId:0,
      noLink:true
    })
    if(checkedIndex == 0){
      event.preventDefault();
    }
  }



 
})


var displayerContainer = new Map();

ipcMain.on('openDisplayer',(event,data)=>{

  let w = displayerContainer.get(data.id);
  if(w){
    w.show();
    return;
  }
  let displayer = new BrowserWindow({
    title:'监视器-' + data.symbol.toUpperCase(),
    icon: path.join(__static, './favicon.ico'),
    parent:win,
    width: 900,
    height: 620,
    minWidth:900,
    minHeight:620,
    show:false,
    useContentSize:true,
    backgroundColor:'#F7F8FA',
    webPreferences: {
      nodeIntegration: true,
      enableRemoteModule: true
    },
   
  });

  displayer.once('ready-to-show',() => {
    displayer.show()
  })


  if (process.env.WEBPACK_DEV_SERVER_URL) {
    displayer.loadURL(process.env.WEBPACK_DEV_SERVER_URL+'displayer?taskId=' + data.id);
     displayer.webContents.openDevTools();
  } else {
    displayer.loadURL('app://./displayer.html?taskId=' + data.id);
  }

  
  
  displayer.on('closed', () => {
    displayer = null;
    displayerContainer.delete(data.id);
  });
  displayerContainer.set(data.id,displayer)
 
})


// ==============================================交易记录==================================================
let tradeWin = null;
ipcMain.on('openTrade',(event,data)=>{
  openTradeWin();
})

function openTradeWin(){
  if(tradeWin != null){
    tradeWin.show();
    return;
  }
   tradeWin = new BrowserWindow({
    title:'交易报表',
    icon: path.join(__static, './favicon.ico'),
    parent:win,
    width: 900,
    height: 600,
    minWidth:900,
    minHeight:600,
    show:false,
    useContentSize:true,
    backgroundColor:'#F7F8FA',
    webPreferences: {
      nodeIntegration: true,
      enableRemoteModule: true
    },
   
  });

  tradeWin.on('ready-to-show',() => {
    tradeWin.maximize()
    tradeWin.show()
    // tradeWin.setFullScreen(true);

  })


  if (process.env.WEBPACK_DEV_SERVER_URL) {
    tradeWin.loadURL(process.env.WEBPACK_DEV_SERVER_URL+'trade');
    tradeWin.webContents.openDevTools();
  } else {
    tradeWin.loadURL('app://./trade.html');
    // tradeWin.webContents.openDevTools();
  }

  
  
  tradeWin.on('closed', () => {
    tradeWin = null;
  });
}



// ==============================================行情==================================================
let marketWin = null;
ipcMain.on('openMarket',(event,data)=>{
  openMarketWin();
 
})

function openMarketWin(){
  if(marketWin != null){
    marketWin.show();
    return;
  }
  marketWin = new BrowserWindow({
    title:'行情分析',
    icon: path.join(__static, './favicon.ico'),
    parent:win,
    width: 1200,
    height: 700,
    minWidth:900,
    minHeight:500,
    show:false,
    useContentSize:true,
    backgroundColor:'#F7F8FA',
    webPreferences: {
      nodeIntegration: true,
      enableRemoteModule: true
    },
   
  });

  marketWin.on('ready-to-show',() => {
    marketWin.show()
  })


  if (process.env.WEBPACK_DEV_SERVER_URL) {
    marketWin.loadURL(process.env.WEBPACK_DEV_SERVER_URL+'market');
    // marketWin.webContents.openDevTools();
  } else {
    marketWin.loadURL('app://./market.html');
  }

  
  
  marketWin.on('closed', () => {
    marketWin = null;
  });
}



// ==============================================检查授权==================================================
//每2小时（7200000）检查一次授权码是否过期
function loopCheckAuth(){
  setInterval(()=>{
    if(win){
     win.webContents.send('checkAuth');
    }
  },7200000)
}


ipcMain.on('closeAllTask', () => {
for(let taskId of tasks.keys()){
    let task = tasks.get(taskId);
    if(task){
      task.shutdown();
    }
}
  
})



// ==============================================检查更新==================================================


let updateWin = null;
function openUpdateWin(){
  if(updateWin != null){
    updateWin.show();
    return;
  }
  updateWin = new BrowserWindow({
    title:'下载进度',
    icon: path.join(__static, './favicon.ico'),
    // parent:win,
    width: 700,
    height: 150,
    maxWidth: 700,
    maxHeight: 150,
    minWidth:700,
    minHeight:150,
    minimizable:false,
    maximizable:false,
    show:false,
    useContentSize:true,
    backgroundColor:'#F7F8FA',
    webPreferences: {
      nodeIntegration: true,
      enableRemoteModule: true
    },
   
  });

  if (process.env.WEBPACK_DEV_SERVER_URL) {
    updateWin.loadURL(process.env.WEBPACK_DEV_SERVER_URL+'update');
    // updateWin.webContents.openDevTools();
  } else {
    updateWin.loadURL('app://./update.html');
    // updateWin.webContents.openDevTools();
  }

  updateWin.on('closed', () => {
    updateWin = null;
  });
}


function checkUpdates(){
  setTimeout(() => {
    // 检测是否有更新
    autoUpdater.checkForUpdates()
  }, 3000)
}
// 发送消息给渲染线程
autoUpdater.autoDownload = false // 关闭自动更新
autoUpdater.autoInstallOnAppQuit = true // APP退出的时候自动安装
autoUpdater.setFeedURL({
  provider: 'generic',
  url: 'http://update.giteasy.cn/update/' // 保证该地址下面包含lasted.yml文件和需要更新的exe文件
})
//当开始检查更新的时候触发。
autoUpdater.on('checking-for-update', () => {
  console.log('checking-for-update...')
})
autoUpdater.on('update-available', (info) => {
  // 可以更新版本
  let message = `发现新版本【${info.version}】，是否更新？`
  let checkedIndex = dialog.showMessageBoxSync({
    icon: path.join(__static, './favicon.ico'),
    title:'更新提示',
    message:message,
    buttons:['以后再说','立即更新'],
    defaultId:1,
    cancelId:0,
    noLink:true
  })
  if(checkedIndex == 1){
    autoUpdater.downloadUpdate()
    openUpdateWin();
  }
  
})
//当没有可用更新的时候触发。
autoUpdater.on('update-not-available', (info) => {
  // console.log('update-not-available',info)
  if(!isAutoCheckUpdate){
    dialog.showMessageBoxSync({
      icon: path.join(__static, './favicon.ico'),
      title:'检查更新',
      message:'当前没有可用的更新。',
      buttons:['确定'],
      defaultId:0,
      noLink:true
    })
  }
})
//当更新发生错误的时候触发。
// autoUpdater.on('error', (err) => {
//   console.log('autoUpdater-error',err)
// })
 // 正在下载的下载进度
autoUpdater.on('download-progress', (progressObj) => {
  if(updateWin != null){
    updateWin.show();
    updateWin.webContents.send('download-progress', progressObj)
  }

 
})
 // 下载完成
autoUpdater.on('update-downloaded', (info) => {
  if(updateWin != null){
    updateWin.show();
    updateWin.webContents.send('update-downloaded')
  }
  
})

// 退出程序
ipcMain.on('quitAndInstall', () => {
  isUpdateQuit = true;
  autoUpdater.quitAndInstall()
})

ipcMain.on('checkUpdate', () => {
  isAutoCheckUpdate = false;
  autoUpdater.checkForUpdates();
})

ipcMain.on('closeUpdateWin', () => {
  if(updateWin != null){
    updateWin.close();
  }
})

// ==============================================关于==================================================
ipcMain.on('app-info',(event,arg)=>{
  let data = process.versions;
  data.productName = packageJson.productName;
  data.author = packageJson.author;
  data.version = packageJson.version;
  data.description = packageJson.description;
  event.reply('app-info-res',data)
})

ipcMain.on('openAbout',(event,arg)=>{
  openAboutWin();
})

let aboutWin = null;
function openAboutWin(){
  if(aboutWin != null){
    aboutWin.show();
    return;
  }
  aboutWin = new BrowserWindow({
    title:'关于',
    icon: path.join(__static, './favicon.ico'),
    parent:win,
    width: 600,
    height: 300,
    maxWidth: 600,
    maxHeight: 300,
    minWidth:600,
    minHeight:300,
    minimizable:false,
    maximizable:false,
    show:false,
    useContentSize:true,
    backgroundColor:'#F7F8FA',
    webPreferences: {
      nodeIntegration: true,
      enableRemoteModule: true
    },
   
  });

  aboutWin.on('ready-to-show',() => {
    aboutWin.show()
  })

  if (process.env.WEBPACK_DEV_SERVER_URL) {
    aboutWin.loadURL(process.env.WEBPACK_DEV_SERVER_URL+'about');
    // aboutWin.webContents.openDevTools();
  } else {
    aboutWin.loadURL('app://./about.html');
    // updateWin.webContents.openDevTools();
  }

  aboutWin.on('closed', () => {
    aboutWin = null;
  });
}

// ==============================================一键卖出所有==================================================

class Message{
  constructor(id,datetime,message){
      this.id = id;
      this.datetime = datetime;
      this.message = message;
      
  }
}

ipcMain.on('sellAll',async (event,id)=>{
  let doc = await db.task.findOne({ _id: id });
  if(doc.state == 1){
    onMessage(new Message(id,moment().format('YYYY-MM-DD HH:mm:ss'),'【一键卖出】任务正在运行中不允许执行一键卖出。'));
    return;
  }
  sellAll(doc);



})

function myRound(num,precision){
  if(precision == undefined || precision <= 0){
      return math.floor(num).toString();
  }else{
      let mulNum = '1'
      for(let i= 0;i < parseInt(precision);i++){
          mulNum += '0';
      }
     
      let tempNum = math.chain(math.bignumber(num)).multiply(math.bignumber(mulNum)).done();
      tempNum =   math.floor(tempNum)
      tempNum = math.chain(math.bignumber(tempNum)).divide(math.bignumber(mulNum)).done();
      return tempNum.toString();
  }

}

let Sleep = function (delay) {
  return new Promise((resolve, reject) => { 
   setTimeout(() => { 
     try {
      resolve(1)
     } catch (e) {
      reject(0)
     }
   }, delay);
  })
 }


async function sellAll (data){
  let symbol = global.CONFIG.huobi.symbolMap.get(data.symbol);
  let success = false;
  let sellAmount = "0";
  
  if(data.accountBalance){
    let datetime = moment().format('YYYY-MM-DD HH:mm:ss');
    onMessage(new Message(data._id,datetime,'【一键卖出】待卖出仓位余额查询...'));
    let balanceResult = await spotAPI.getOneBalance({'currency':symbol['base-currency']});
    if(balanceResult.success){
        sellAmount = myRound(balanceResult.data,symbol['amount-precision']);
        success = true;
    }
  }

  if(!success){
      if(data.accountBalance){
        let datetime = moment().format('YYYY-MM-DD HH:mm:ss');
        onMessage(new Message(data._id,datetime,'【一键卖出】计算待卖出数量...'));
      }
      sellAmount = myRound(data.totalAmount,symbol['amount-precision'])
  }

if(parseFloat(sellAmount) <= 0){
  onMessage(new Message(data._id,moment().format('YYYY-MM-DD HH:mm:ss'),'【一键卖出】数量不能小于等于0。'));
  return;
}


  let orderId = null;
  let sellResult = await spotAPI.sellByMarket({symbol:data.symbol,amount:sellAmount})
  if(sellResult.success){
       let datetime = moment().format('YYYY-MM-DD HH:mm:ss');
        onMessage(new Message(data._id,datetime,'【一键卖出】下单成功'));
       orderId = sellResult.data;
  }else{
    let datetime = moment().format('YYYY-MM-DD HH:mm:ss');
     onMessage(new Message(data._id,datetime,'【一键卖出】下单失败：' + sellResult.data));
     return;
  }
  if(!orderId){
    return;
  }

   // 查询订单状态
   let datetime = moment().format('YYYY-MM-DD HH:mm:ss');
     onMessage(new Message(data._id,datetime,'【一键卖出】查询订单状态...'));
 
     for(let n = 1; n <= 10; n++){
      await Sleep(2000);
      let orderResult = await spotAPI.getOrder({orderId:orderId})

      if(orderResult.success){
          if(orderResult.data.state == 'filled'){
            onMessage(new Message(data._id,moment().format('YYYY-MM-DD HH:mm:ss'),'【一键卖出】订单交易完成。'));

              let fieldCashAmount = orderResult.data['field-cash-amount'];
              let fieldFees = orderResult.data['field-fees'];
              //保存订单祥情
              let currentMoney = math.chain(math.bignumber(fieldCashAmount)).subtract(math.bignumber(fieldFees)).done();
              currentMoney = math.round(currentMoney,symbol['value-precision']).toString()
              let trade = {
                  taskId:data._id,
                  symbol:data.symbol,
                  loopCount:data.loopCount,
                  status:0,
                  orderId:orderId,
                  type:'sell-market',
                  tradeMode:2,
                  direction:'sell',
                  money:currentMoney,
                  amount:sellAmount,
                  baseCurrency:symbol['base-currency'],
                  quoteCurrency:symbol['quote-currency']
              }
              await db.trade.insert(trade);
              
              //并且更新本轮订单状态为已完成
              await db.trade.update({ taskId: data._id,loopCount:data.loopCount },{ $set:{
                  status:1,
              }},{ multi: true });


             

              let loopCount = data.loopCount + 1;
               //更新任务状态
               await db.task.update({ _id: data._id }, {$set: {
                  buyTradeCount:0,
                  totalMoney:'0',
                  totalAmount:'0',
                  loopCount:loopCount
               }});

                //查询本轮交易明细，生成交易报告
              common.insertReport(data._id,data.loopCount);
            
               onUpdate(new Message(data._id,moment().format('YYYY-MM-DD HH:mm:ss'),null))
              break;
          }else{
            onMessage(new Message(data._id,moment().format('YYYY-MM-DD HH:mm:ss'),'【一键卖出】等待订单完成...'));
              n--;
          }
          
      }else{
        onMessage(new Message(data._id,moment().format('YYYY-MM-DD HH:mm:ss'),'【一键卖出】订单查询失败...'));
      }
   }




}

